/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : game_page.c
  * @brief          : select game
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include <stdlib.h>
#include "main.h"
#include "fatfs.h"
#include "lcd.h"
#include "mp3play.h"
#include "game_page.h"

#define OK_KEY_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8)

static u32 Game_Buff[10] = {0}; // 10 * 20 

static long long LCD_Buff[128] = { 0 };//128 * 64
static long long Pixel_Buff[5] = { 0x1F, 0x11, 0x15, 0x11, 0x1F};// one cube

static void Game_Buff_to_LCD_Buf(void)
{
	for(int i=0; i <10; i++)
	{
		for(int j=0; j <20; j++)
		{
			if(Game_Buff[i] & 0x01 << j)
			{
				for(int k = 0; k < 5; k++)
				{
					LCD_Buff[127 -(j * 5 + k)] |= (Pixel_Buff[k] << (i * 5));
				}
			}
		}
	}
	
	for(int i = 127; i > 26; i--) // right side line
	{
		LCD_Buff[i] |= 0x4000000000000;
	}
	
	LCD_Buff[127 - 100] = 0x7FFFFFFFFFFFF; // bottom side line 
}
static void Preview_Box_to_LCD_Buff(void);
static void Score_to_LCD_Buff(void);
static void LCD_Refresh(void)
{
	for(int i=0; i <128; i++)
	{
		LCD_Buff[i] = 0;
	}
	
	Game_Buff_to_LCD_Buf();
	Preview_Box_to_LCD_Buff();		
	Score_to_LCD_Buff();
	
	for(int i=0; i <8; i++)
	{
		LCD_Set_Pos(0, i);
		for(int j = 0; j < 128; j++)
		{
			LCD_Write_Data(LCD_Buff[j] >> (i * 8));
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

static const u16 boxs[7][4] = 
{
	{0x0E40,0x4C40,0x4E00,0x4640},
	{0x4C80,0xC600,0x4C80,0xC600},
	{0x8C40,0x6C00,0x8C40,0x6C00},
	{0xE800,0xC440,0x2E00,0x4460},
	{0x8E00,0xC880,0xE200,0x2260},
	{0x0F00,0x4444,0x0F00,0x4444},
	{0xCC00,0xCC00,0xCC00,0xCC00}
};

static const u32 full_line_data[20] = 
{
	0x00000001,0x00000002,0x00000004,0x00000008,0x00000010,0x00000020,0x00000040,0x00000080,
	0x00000100,0x00000200,0x00000400,0x00000800,0x00001000,0x00002000,0x00004000,0x00008000,
	0x00010000,0x00020000,0x00040000,0x00080000
};

u16 box = 0;
static unsigned int random_number;//

u8 box_index = 0, shape_index = 1;
u8 next_box_index = 0, next_shape_index = 1;

s8 x = 4, y=0;

static unsigned char i,j;

static int score = 0;
static int Box_Drop_Speed = 400; // mS

u32 Tetris_Move_Buff[10] = { 0 };
u32 Tetris_Static_Buff[10] = { 0 };

static void Preview_Box_to_LCD_Buff()
{
	u16 next_box = boxs[next_box_index][next_shape_index];
	
	for(int i = 0; i < 4; i ++)
	{
		for(int j = 0; j < 4; j++)
		{
			if(next_box & (0x01 << (i * 4 + j)))
			{
				for(int k = 0; k < 5; k++)
				{
					LCD_Buff[22 - (j * 5 + k)] |= (Pixel_Buff[k] << (20 - (i *5)));
				}
			}
		}
	}
}

static const unsigned char num_font[10][7] = 
{
	{0x06,0x09,0x09,0x09,0x09,0x09,0x06},//0
	{0x0f,0x04,0x04,0x04,0x04,0x06,0x04},//1
	{0x0f,0x01,0x06,0x08,0x08,0x09,0x06},//2
	{0x06,0x09,0x08,0x06,0x08,0x09,0x06},//3
	{0x04,0x04,0x0F,0x05,0x05,0x06,0x04},//4
	{0x06,0x09,0x08,0x08,0x07,0x01,0x0F},//5
	{0x06,0x09,0x09,0x07,0x01,0x09,0x06},//6
	{0x02,0x02,0x02,0x02,0x04,0x08,0x0F},//7
	{0x06,0x09,0x09,0x06,0x09,0x09,0x06},//8
	{0x06,0x09,0x08,0x06,0x09,0x09,0x06} //9
};

static void Score_to_LCD_Buff(void)
{
	int tmp = score, quot = 0;
	for(i=0; i < 5; i ++)
	{
		quot = tmp % 10;
		tmp /= 10;
		for(j =0; j < 7; j ++)
		{
			LCD_Buff[j + 2] |=  (long long)num_font[quot][j] << (58 - (i * 5));
		}
	}
}

void Tetris_Init(void)
{
	random_number = rand();
	box_index = random_number%7;
	shape_index = random_number%4;
	
	random_number = rand();
	next_box_index = random_number%7;
	next_shape_index = random_number%4;
	
	Box_Drop_Speed = 400;
	score = 0;		
	x = 4;
	y = -3;
}

static void Trtris_Reset(void)
{
	for(i=0; i<10; i++) //
	{
		Tetris_Move_Buff[i] = 0;
		Tetris_Static_Buff[i] = 0;
	}
	Box_Drop_Speed = 400;
	score = 0;		
	x = 4;
	y = -3;
}

static u8 Removable_Check(s8 x, s8 y, u16 box)
{
	if(y == 18) return 0;
	if(y == 17)	if(box & 0x8888) return 0;
	
	if(x == -2) return 0;
	if(x == -1) if(box & 0xF000)return 0;
	
	if(x == 10)return 0;
	if(x == 9)if(box & 0x0F00)return 0;
	if(x == 8)if(box & 0x00F0)return 0;
	if(x == 7)if(box & 0x000F)return 0;
	
	if(x>=0 && x < 10)    {Tetris_Move_Buff[x]= box>>12;      				if(y>=0)Tetris_Move_Buff[x]<<=y;  else Tetris_Move_Buff[x]>>=(0-y);}
	if(x+1>=0 && x+1 < 10){Tetris_Move_Buff[x+1]= (box&0x0F00) >> 8;  if(y>=0)Tetris_Move_Buff[x+1]<<=y;else Tetris_Move_Buff[x+1]>>=(0-y);}
	if(x+2>=0 && x+2 < 10){Tetris_Move_Buff[x+2]= ((box&0xF0)>>4);  	if(y>=0)Tetris_Move_Buff[x+2]<<=y;else Tetris_Move_Buff[x+2]>>=(0-y);}
	if(x+3>=0 && x+3 < 10){Tetris_Move_Buff[x+3]= (box&0x0F);         if(y>=0)Tetris_Move_Buff[x+3]<<=y;else Tetris_Move_Buff[x+3]>>=(0-y);}
	
	y = x + 4; 
	if(x < 0) x = 0;
	if(y > 10) y = 10;
	
	for(;x < y; x++)
	{
		if(Tetris_Static_Buff[x] & Tetris_Move_Buff[x]) return 0;
	}
	return 1;
}

static void Full_Line_Check(void)
{
	u8 full_line[4] = {0}, full_line_count = 0;
	
	for(i=0;i < 10; i++)
	{
		Tetris_Static_Buff[i] |= Tetris_Move_Buff[i];
		Tetris_Move_Buff[i] = Tetris_Static_Buff[i];//
	}

	i=y; y+=4; if(y>20)y=20;
	for(;i < y; i++) //
	{
		for(j=0;j<10;j++)
		{
			if((Tetris_Static_Buff[j] & full_line_data[i]) == 0)break;
		}
		if(j == 10)
		{
			full_line[full_line_count++] = i; //
			score ++;
		}
	}
	
	for(i=0; i < full_line_count; i++) //
	{
		for(j=0;j<10;j++)
		{
			Tetris_Static_Buff[j] &= ~full_line_data[full_line[i]];
		}
	}
	for(i=0;i<10;i++) //
	{
		Game_Buff[i] = Tetris_Static_Buff[i];
	}
	
	LCD_Refresh();
	HAL_Delay(100);
	
	for(i=0; i < full_line_count; i++) //
	{
		for(j=0;j<10;j++)
		{
			Tetris_Static_Buff[j] |= full_line_data[full_line[i]];
		}
	}
	for(i=0;i<10;i++) //
	{
		Game_Buff[i] = Tetris_Static_Buff[i];
	}
	LCD_Refresh();
	HAL_Delay(100);
	
	for(i=0; i < full_line_count; i++) //
	{
		for(j=0;j<10;j++)
		{
			Tetris_Static_Buff[j] &= ~full_line_data[full_line[i]];
		}
	}
	for(i=0;i<10;i++) //
	{
		Game_Buff[i] = Tetris_Static_Buff[i];
	}
	LCD_Refresh();
	HAL_Delay(100);
	
	for(i=0; i < full_line_count; i++) //
	{
		for(j=0; j<10; j++)
		{
			Tetris_Static_Buff[j] |= full_line_data[full_line[i]];
		}
	}
	for(i=0; i<10; i++) //
	{
		Game_Buff[i] = Tetris_Static_Buff[i];
	}
	LCD_Refresh();
	
	for(i=0; i < full_line_count; i++) 
	{
		for(j=0;j<10;j++)
		{
			Tetris_Static_Buff[j] &= ((unsigned long)(0xfffff) << (full_line[i]+1));
			Tetris_Move_Buff[j] &=   ((unsigned long)(0xfffff) >> (20-full_line[i]));

			Tetris_Move_Buff[j] <<= 1;
			
			Tetris_Static_Buff[j] |= Tetris_Move_Buff[j];
			Tetris_Move_Buff[j] = Tetris_Static_Buff[j];//
		}
	}
	
	full_line_count = 0;
}


static u8 Game_Over_Check(void)
{
	for(i=0; i<10; i++) //
	{
		if(Tetris_Static_Buff[i] & 0x01) return 1;
	}
	return 0;
}

void game_tetris_loop(void)
{
	volatile unsigned char is_Bottom = 0;
	static volatile uint32_t Last_Drop_Tick = 0;
	
	if(g_key_value == 5)
	{
		g_key_value = 0;
		while(1)
		{
			if(g_key_value == 5) // return
			{
				g_main_while_loop = game_page_loop;
				g_key_value = 99;
				Trtris_Reset();
				return;
				
			}
			if(g_key_value == 1)
			{
				g_key_value = 0;
				break;
			}
			HAL_Delay(100);
		}
	}
	
	for( i =0; i <10; i++)
	{
		Tetris_Move_Buff[i] = 0;
	}
	
	if(g_key_value == 5 )// pause
	{
		HAL_Delay(300);
		g_key_value = 0;
		while(g_key_value != 5)
		{
			HAL_Delay(100);
		}
	}
	if(g_key_value == 1 )// rotating
	{
		i = shape_index +1;
		if(i == 4) i = 0;
		
		box = boxs[box_index][i];
		if(Removable_Check(x, y, box)) shape_index++;
		
		if(shape_index == 4)
			shape_index = 0;
	}
	
	box = boxs[box_index][shape_index];
	
	if(g_key_value == 2) // left
	{
		if(Removable_Check(x-1, y, box)) x --;
	}
	
	if(g_key_value == 3)// right
	{
		if(Removable_Check(x+1, y, box)) x ++;
	}
	
	is_Bottom = 0;
	if(g_key_value == 4) // drop to the bottom
	{
		while(Removable_Check(x, y+1, box)) 
		{
			y ++;
		}
		is_Bottom = 1;
	}
	else if(HAL_GetTick() - Last_Drop_Tick > Box_Drop_Speed) // this value  determines the speed of the fall
	{
		Last_Drop_Tick = HAL_GetTick();
		if(Removable_Check(x, y+1, box)) y ++;
		else //
		{
			is_Bottom = 1;// 
		}
	}
	g_key_value = 0;
	
	if(x>=0 && x < 10)    {Tetris_Move_Buff[x]= box>>12;      		if(y>=0)Tetris_Move_Buff[x]<<=y;  else Tetris_Move_Buff[x]>>=(0-y);}
	if(x+1>=0 && x+1 < 10){Tetris_Move_Buff[x+1]= (box&0x0F00) >> 8;if(y>=0)Tetris_Move_Buff[x+1]<<=y;else Tetris_Move_Buff[x+1]>>=(0-y);}
	if(x+2>=0 && x+2 < 10){Tetris_Move_Buff[x+2]= ((box&0xF0)>>4);  if(y>=0)Tetris_Move_Buff[x+2]<<=y;else Tetris_Move_Buff[x+2]>>=(0-y);}
	if(x+3>=0 && x+3 < 10){Tetris_Move_Buff[x+3]= (box&0x0F);       if(y>=0)Tetris_Move_Buff[x+3]<<=y;else Tetris_Move_Buff[x+3]>>=(0-y);}
	
	if(is_Bottom == 1) //
	{
		Full_Line_Check();
		if(Game_Over_Check() == 1) // Game Over
		{
			LCD_Disp_Str(16, 6, "   Game Over   ", 0);
			while(g_key_value != 1) // press OK Key Resart
			{
				HAL_Delay(100);
			}
			Trtris_Reset();
		}
		random_number = rand();
		
		box_index = next_box_index;
		next_box_index = random_number % 7;
		
		random_number = rand();
		shape_index = next_shape_index;
		next_shape_index = random_number % 4;
		x = 4;
		y = -3;

		if(Box_Drop_Speed > 100)Box_Drop_Speed -= 5;
	}

	for(i = 0; i < 10; i++) //
	{
		Game_Buff[i] = Tetris_Static_Buff[i] | Tetris_Move_Buff[i];
	}
	LCD_Refresh();
}
